package com.intelligent.ispc.utils;

import com.google.common.collect.Lists;
import com.intelligent.ispc.common.utils.ApplicationException;
import com.intelligent.ispc.common.utils.ExceptionMessages;
import com.intelligent.ispc.common.utils.ResourceBundleUtils;
import com.intelligent.ispc.common.utils.StringUtils;
import com.intelligent.ispc.core.dto.DynSearchFieldDto;
import com.intelligent.ispc.foundation.parser.FilterExpressionParser;
import com.intelligent.ispc.foundation.parser.ParserToken;
import com.intelligent.ispc.foundation.parser.SEPResult;

import java.util.*;

/**
 * Created by julia on 16/8/24.
 */
public class SpcFilterUtil {
    /**
     * Filter the data.
     *
     * @param searchKeys the keys of search
     * @param row the row of data
     * @return  true, exist; false, not exist
     */
    public boolean filterData(List<DynSearchFieldDto> searchKeys, Map<String, String> row) {
        boolean isExist = true;
        try {
            for (DynSearchFieldDto dynSearchFieldDto : searchKeys) {
                if (dynSearchFieldDto.isAdvance()) {
                    FilterExpressionParser fep = new FilterExpressionParser();
                    SEPResult result = fep.doParser(dynSearchFieldDto.getValue());
                    List<Object> validateResult = parseSepResult(result, row);
                    if (validateResult == null || validateResult.isEmpty()) {
                        isExist = false;
                    }
                } else {
                    String value = dynSearchFieldDto.getValue();
                    String name = dynSearchFieldDto.getName();
                    String token = dynSearchFieldDto.getToken();
                    if (!filterBaseData(name, value, token, row)) {
                        isExist = false;
                    }
                }
            }
        } catch (Exception e) {
            throw new ApplicationException(ExceptionMessages.ERR_15001, ResourceBundleUtils.getString(ExceptionMessages.EXCEPTION_GLOBAL_SEARCH_GRAMMAR_INVALID));
        }

        return isExist;
    }

    /**
     * Parse the Sep Result.
     *
     * @param result the Sep Result
     * @param row the row of data
     * @return  the object list
     */
    public List<Object> parseSepResult(SEPResult result, Map<String, String> row) {
        List<Object> resultList = Lists.newLinkedList();
        List<Object> leftList = Lists.newLinkedList();
        List<Object> rightList = Lists.newLinkedList();
        if (result.getLeftExpr() instanceof SEPResult) {
            List left = parseSepResult((SEPResult) result.getLeftExpr(), row);
            leftList.addAll(left);
            resultList.addAll(left);
        }
        if (result.getRightExpr() instanceof SEPResult) {
            List right = parseSepResult((SEPResult) result.getRightExpr(), row);
            rightList.addAll(right);
            resultList.addAll(right);
        }

        if (StringUtils.isBlank(result.getLogicalToken())) {
            resultList.addAll(parseBaseData(result, row));
        } else {
            if (result.getLogicalToken().equals(ParserToken.OR.getCode())) {
                return resultList;
            }

            if (leftList != null && !leftList.isEmpty() && rightList != null && !rightList.isEmpty()
                    && result.getLogicalToken().equals(ParserToken.AND.getCode())) {
                return resultList;
            } else {
                resultList.clear();
                return resultList;
            }
        }
        return resultList;
    }

    /**
     * Parse the Sep Result.
     *
     * @param result the Sep Result
     * @param row the row of data
     * @return  the object list
     */
    public List<Object> parseBaseData(SEPResult result, Map<String, String> row) {
        List<Object> resultList = Lists.newLinkedList();
        if (StringUtils.isBlank(result.getLogicalToken())) {
            if (filterBaseData((String) result.getLeftExpr(), (String) result.getRightExpr(), result.getToken(), row)) {
                resultList.add(row);
            }
        }
        return resultList;
    }

    /**
     * Parse the Sep Result.
     *
     * @param name the column
     * @param value the value of column
     * @param token the token
     * @param row the row of data
     * @return  true, exist; false, not exist
     */
    public boolean filterBaseData(String name, String value, String token, Map<String, String> row) {
        boolean isExist = true;
        if (row.containsKey(name) && StringUtils.isNotBlank(token) && StringUtils.isNotBlank(value)) {
            String realValue = row.get(name);
            if (StringUtils.isBlank(realValue)) {
                isExist = false;
            } else {
                switch (ParserToken.getValueOf(token)) {
                    case EQ:
                        if (!realValue.equals(value)) {
                            isExist = false;
                        }
                        break;
                    case LIKE:
                        if (!realValue.contains(value)) {
                            isExist = false;
                        }
                        break;
                    case GT:
                        if (StringUtils.isNumeric(realValue) && StringUtils.isNumeric(value)) {
                            if (Double.valueOf(realValue) <= Double.valueOf(value)) {
                                isExist = false;
                            }
                        } else {
                            isExist = false;
                        }
                        break;
                    case GET:
                        if (StringUtils.isNumeric(realValue) && StringUtils.isNumeric(value)) {
                            if (Double.valueOf(realValue) < Double.valueOf(value)) {
                                isExist = false;
                            }
                        } else {
                            isExist = false;
                        }
                        break;
                    case LT:
                        if (StringUtils.isNumeric(realValue) && StringUtils.isNumeric(value)) {
                            if (Double.valueOf(realValue) >= Double.valueOf(value)) {
                                isExist = false;
                            }
                        } else {
                            isExist = false;
                        }
                        break;
                    case LET:
                        if (StringUtils.isNumeric(realValue) && StringUtils.isNumeric(value)) {
                            if (Double.valueOf(realValue) > Double.valueOf(value)) {
                                isExist = false;
                            }
                        } else {
                            isExist = false;
                        }
                        break;
                    case NE:
                        if (realValue.equals(value)) {
                            isExist = false;
                        }
                        break;
                    default:
                        break;
                }
            }
        }
        return isExist;
    }

    /**
     * Parse the search key.
     *
     * @param searchKeys the keys of search
     * @return  the String set
     */
    public Set<String> parseSearchKey(List<DynSearchFieldDto> searchKeys) {
        Set<String> resultList = new LinkedHashSet<>();
        try {
            for (DynSearchFieldDto dynSearchFieldDto : searchKeys) {
                if (dynSearchFieldDto.isAdvance()) {
                    FilterExpressionParser fep = new FilterExpressionParser();
                    SEPResult result = fep.doParser(dynSearchFieldDto.getValue());
                    if (result.getLeftExpr() instanceof SEPResult) {
                        parseSepResult((SEPResult) result.getLeftExpr(), null);
                    }
                    if (result.getRightExpr() instanceof SEPResult) {
                        parseSepResult((SEPResult) result.getRightExpr(), null);
                    }

                    if (StringUtils.isBlank(result.getLogicalToken())) {
                        resultList.add((String) result.getLeftExpr());
                    }
                } else {
                    String name = dynSearchFieldDto.getName();
                    resultList.add(name);
                }
            }
        } catch (Exception e) {
            throw new ApplicationException(ExceptionMessages.ERR_15001, ResourceBundleUtils.getString(ExceptionMessages.EXCEPTION_GLOBAL_SEARCH_GRAMMAR_INVALID));
        }
        return resultList;
    }
}
